#ifndef GRAPH_H
#define GRAPH_H

#include "RecordDatabase.h"

#include <QDateTime>
#include <QGraphicsView>

class Graph
{
private:
    QList<QPoint> data;
};

/*!
 * @class RegLine
 * @brief Represents a linear regression line. Can calculate linear regression
 *        from graph data.
 *
 */

class RegLine
{
public:
    RegLine();

    /*! Calculates linear regression of given data using least squares method.
     * @param type Field of record to use
     * @param start Iterator pointing to start of record list
     * @param end Iterator pointing to end of record list
     */
    void calcLinReg(Record::RecordField type,
                    QList<Record>::Iterator start,
                    QList<Record>::Iterator end);

    /*! Returns point on linear regression line at a given time.
     * @param time Time on x-axis
     * @return Y value of point on regression line at the given time.
     */
    double getY(const QDateTime time);

    /*! Gets string of the unit type represented by the regression line.
     */
    QString getUnitString();

    double slope;
    double intercept;

    QDateTime startTime;
    QDateTime endTime;

private:
    QString unitString;
};

/*class SelectionOverlay : public QWidget
{
	Q_OBJECT

public:
	explicit SelectionOverlay(QWidget *parent = 0);


	void paintEvent(QPaintEvent *);
};*/

/*!
 * @class GraphDisplay
 * @brief Displays record data in graph form on a widget.
 *
 */

class GraphDisplay : public QWidget
{
    Q_OBJECT
public:
    explicit GraphDisplay(QWidget *parent = 0);

    /*! Sets the record data to plot.
     * @param data List of records to plot.
     */
    void setRecordData(QList<Record> data);

    /*! Sets type of data to display from graph.
     * @param type Data type to graph
     */
    void setRecordType(const Record::RecordField type);

    /*! Sets the start and end dates (on X axis) to plot records.
     * @param timeStart Start time and date.
     * @param timeEnd End time and date.
     */
    void setDateRange(const QDateTime timeStart,
                      const QDateTime timeEnd);

    /*! Sets vertical range of record plot.
     * @param start Low value.
     * @param end High value.
     */
    void setValueRange(float start, float end);

    /*! Sets date range to display full graph data.
     */
    void resetDateRange();

    /*! Gets the currently displayed start date in the graph
     */
    QDateTime getStartDate();

    /*! Gets the currently displayed end date in the graph
     */
    QDateTime getEndDate();

    /*! Gets regression line data (if regression line was created)
     * @relates showRegLine
     */
    RegLine getRegressionLine();

    /*! Calculates and displays a linear regression line for the current data
     *  range
     */
    void showRegLine();

    /*! Hides linear regression line (if displayed)
     */
    void hideRegLine();
    
signals:
    void rangeChanged(QDateTime start, QDateTime end);
    
public slots:
    void resizeEvent(QResizeEvent * event);

protected:
    void mousePressEvent(QMouseEvent * event);
    void mouseMoveEvent(QMouseEvent * event);
    void mouseReleaseEvent(QMouseEvent * event);

    void wheelEvent(QWheelEvent * event);

    void paintEvent(QPaintEvent *);

private:
    enum IntervalType {
        INTERVAL_TYPE_MINUTES = -1,
        INTERVAL_TYPE_HOURS,
        INTERVAL_TYPE_DAYS,
        INTERVAL_TYPE_MONTHS,
        INTERVAL_TYPE_YEARS,

        INTERVAL_TYPE_COUNT
    };

    //------------------
    // Private functions
    //------------------

    /*! Resize vertical range to fit data values
     */
    void setRangeToDataY();

    /*! Gets the value displayed at the y position on the graph.
     * @param y Y coordinate on graph
     * @relates getPosFromValue
     */
    float getValueFromPos(int y) const;

    /*! Gets the y position on the graph where a value will be displayed.
     * @param value Value on the y-axis
     * @relates getValueFromPos
     */
    int getPosFromValue(float value) const;

    /*! Gets the date displayed at the x position on the graph.
     * @param y X coordinate on graph
     * @relates getPosFromDate
     */
    QDateTime getDateFromPos(int x) const;

    /*! Gets the x position on the graph where a date will be displayed.
     * @param date Date on the x-axis
     * @relates getDateFromPos
     */
    int getPosFromDate(const QDateTime date) const;

    /*! Finds the data point on the graph that is nearest to the specified
     *  coordinate on the graph.
     * @param x X coordinate
     * @param y Y coordinate
     * @param r Record object to store nearest record data
     * @returns Distance (in pixels) from the data point's center. If none is
     *          found, return -1.
     */
    float getNearestDataPoint(int x, int y, Record ** r) const;

    /*! Finds the records that fall between the start and end dates and
     *  updates internal range of records currently displayed.
     * @param start Start date
     * @param end   End date
     */
	void updateValidRange(const QDateTime start, const QDateTime end);

    /*! Draws axis and axis info
     * @note Draw this before drawing graphs
     */
    void drawAxis(QPainter &painter);

    /*! Draws graph of data
     */
    void drawGraph(QPainter &painter);

    /*! Draws linear regression line
     */
    void drawRegLine(QPainter &painter);

    static bool compareRecord(const Record &r1, const Record &r2);

    //---------------------
    // Private data members
    //---------------------

    bool isSelectingRange;
    int selectStart, selectEnd;

    // Graph range
    float rangeYStart, rangeYEnd, rangeY;

    IntervalType intervalType;
    int intervalX, intervalY, intervalXMin, intervalXScale, intervalXOffset;

    QDateTime rangeStart, rangeEnd, markerStart;
    float pixelsPerSec;

    // Graph display
    QRect graphArea;
    int marginBottom, marginLeft, marginTop, marginRight;

    QPen gridVertPen;
    QPen gridHorizPen;
    QPen gridIntervalPen;

	QPen graphPen;

    QBrush bottomBoxBrush;
	QBrush graphBrush;

	QImage graphBuffer;

    // Graph data
    QList<Record> recordList;
    QList<Record>::iterator recordValidBegin, recordValidEnd;
    Record::RecordField recordType;

    bool showRegressionLine;
    RegLine regressionLine;

    float valueMin, valueMax;
    float scalingMarginY;

    Record * hoverRecord;

    int cursorPositionX;
};

#endif // GRAPH_H
